// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// Package execabs是os/exec 
// 的替代品，它需要通过路径查找来找到绝对路径。
// 也就是说，execabs。命令（“cmd”）与exec运行相同的路径查找
// 。命令（“cmd”），但如果结果是相对路径
// 则Run和Start方法将报告
// 错误，而不是运行可执行文件。
package execabs

import (
	"context"
	"fmt"
	"os/exec"
	"path/filepath"
	"reflect"
	"unsafe"
)

var ErrNotFound = exec.ErrNotFound

type (
	Cmd       = exec.Cmd
	Error     = exec.Error
	ExitError = exec.ExitError
)

func relError(file, path string) error {
	return fmt.Errorf("%s resolves to executable relative to current directory (.%c%s)", file, filepath.Separator, path)
}

func LookPath(file string) (string, error) {
	path, err := exec.LookPath(file)
	if err != nil {
		return "", err
	}
	if filepath.Base(file) == file && !filepath.IsAbs(path) {
		return "", relError(file, path)
	}
	return path, nil
}

func fixCmd(name string, cmd *exec.Cmd) {
	if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) {
		// 执行。命令是用一个简单的二进制名称和
		// exec调用的。LookPath返回的路径不是绝对路径。
		// 设置cmd。lookPathErr和clear cmd。路径，使其
		// 无法运行。
		lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer()))
		if *lookPathErr == nil {
			*lookPathErr = relError(name, cmd.Path)
		}
		cmd.Path = ""
	}
}

func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
	cmd := exec.CommandContext(ctx, name, arg...)
	fixCmd(name, cmd)
	return cmd

}

func Command(name string, arg ...string) *exec.Cmd {
	cmd := exec.Command(name, arg...)
	fixCmd(name, cmd)
	return cmd
}
